﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.ComponentModel;

using Microsoft.Practices.Composite.Events;
using Microsoft.Practices.Composite.Modularity;
using Microsoft.Practices.Composite.Presentation.Events;
using Microsoft.Practices.Composite.Regions;
using Microsoft.Practices.Unity;

using Matchingo.Common.Constants;
using Matchingo.Common.Types.Types;
using Matchingo.Common.Types.Interfaces;
using Matchingo.Common.Types.Enumerations;
using Matchingo.Common.Types.EventArguments;
using Matchingo.Common.Types.Events;

namespace Matchingo.Modules.HighScoreModule
{
    public class HighScoreScreenViewModel : DependencyObject, IMenuItem, IModule, INotifyPropertyChanged
    {
        public enum GameMetric
        {
            Score,
            Time
        }

        #region ========== Private Properties ==========

        private readonly IUnityContainer Container;
        private readonly IEventAggregator EventAggregator;
        private readonly IRegionManager RegionManager;
        private readonly IHighScoreProvider ScoreProvider;
        private readonly IMenuFlowProvider FlowProvider;
        private readonly IGameExecutionFacade GameFacade;
        private readonly IGameSettingsProvider SettingsProvider;

        private IHighScoreScreenView View { get; set; }

        #endregion

        #region ========== Public Properties ==========

        private GameMetric _gameMetricFilter;
        public GameMetric GameMetricFilter 
        {
            get { return _gameMetricFilter; }
            private set
            {
                _gameMetricFilter = value;
                NotifyPropertyChanged("GameMetricFilter");
            }
        }

        private int _cardCountFilter;
        public int CardCountFilter 
        {
            get { return _cardCountFilter; }
            set
            {
                _cardCountFilter = value;
                NotifyPropertyChanged("CardCountFilter");
            }
        }

        public List<Game> GameRecordsByScore
        {
            get 
            {
				return ScoreProvider.GetHighScoresByCardCount(10, CardCountFilter);
			}
        }

        public List<Game> GameRecordsByTime
        {
            get 
            {
				var scores = ScoreProvider.GetHighScoresByCardCount(10, CardCountFilter);
				var highScores = (from gr in scores
								  orderby gr.GameTimeElapsed ascending
								  select gr);

				return highScores.ToList();
			}
        }

        #endregion

        #region ========== Constructor ==========

        public HighScoreScreenViewModel(IUnityContainer container,
            IEventAggregator eventAggregator, 
            IRegionManager regionManager, 
            IGameExecutionFacade gameFacade, 
            IMenuFlowProvider flowProvider, 
            IHighScoreProvider scoreProvider,
            IGameSettingsProvider settingsProvider)
        {
            GameMetricFilter = GameMetric.Score;

            Container = container;
            GameFacade = gameFacade;
            ScoreProvider = scoreProvider;            
            RegionManager = regionManager;
            FlowProvider = flowProvider;
            EventAggregator = eventAggregator;
            SettingsProvider = settingsProvider;
        }

        #endregion

        #region ========== Public Methods ==========

        public void Initialize()
        {
            View = Container.Resolve<IHighScoreScreenView>();
            View.Initialize(this);

            // register this UserControl with our UI Flow Provider
			FlowProvider.RegisterUIModule(this, MenuModuleNames.ModuleNames.HighScoreMenu, View.RootElement);

            IRegion region = RegionManager.Regions[CommonConstants.SHELL_REGION_HighScores];
            region.Add(View);

            // TODO:: remove this eventing?
            ScoreProvider.RecordAdded += ScoreProviderRecordAdded;
            ScoreProvider.PropertyChanged += ScoreProviderPropertyChanged;

            EventAggregator.GetEvent<GameEndedEvent>().Subscribe(GameLogicGameEnded, ThreadOption.UIThread, true);
            EventAggregator.GetEvent<SettingsChangedEvent>().Subscribe(OptionsChanged, ThreadOption.UIThread, true);

            CardCountFilter = SettingsProvider.GameSettings.CardCount;
        }

        // TODO:: this should not be necessary - having a hard time getting the enum to display properly, or to convert the string back to enum
        public void SetGameMetricFilter(string filterEnumString)
        {
            switch(filterEnumString)
            {
                case "Score":
                    GameMetricFilter = GameMetric.Score;
                    break;
                case "Time":
                    GameMetricFilter = GameMetric.Time;
                    break;
                default:
                    throw new ArgumentOutOfRangeException(string.Format("HighScoreModule.SetGameMetricFilter() - passed in unknown filter enum - '{0}'", filterEnumString));
            }
        }

        public void ReplayGame(Game g)
        {
            GameFacade.PlaybackGame(g);
        }

        public bool TransitionMenu(InOutEnumeration.InOut inOrOut, DirectionNames.Direction direction, bool skipAnimation)
        {
            if (direction != DirectionNames.Direction.Right)
                return false;

            if (inOrOut == InOutEnumeration.InOut.In)
                View.ShowScreen(skipAnimation);
            else
                View.HideScreen(skipAnimation);

            return true;
        }

        #endregion

        #region ========== Private Methods ==========

        // TODO:: necessary?
        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged == null)
                return;

            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion      

        #region =========== Event Handlers ==========

        private void GameLogicGameEnded(GameEndedEventArgs args)
        {
            // if this wasn't an AI game then select the game record for the game that just ended in the highscore view
            if (args.Game.UniqueID != Guid.Empty && args.Game.ArtificialGame == false)
            {
                View.SelectGameRecord(args.Game.UniqueID);
            }
        }

        void ScoreProviderPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            NotifyPropertyChanged("GameRecords");
        }

        void ScoreProviderRecordAdded(object sender, GameRecordAddedEventArgs e)
        {
            NotifyPropertyChanged("GameRecords");
        }

        private void OptionsChanged(MatchGameSettings gameSettings)
        {
            CardCountFilter = gameSettings.CardCount;
        }

        #endregion

        #region =========== Public Events ==========

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }
}
